diff -urN crypto/heimdal/kdc/config.c heimdal-0.5.3/kdc/config.c
--- crypto/heimdal/kdc/config.c	2003-03-17 07:46:55.000000000 +0100
+++ crypto/heimdal/kdc/config.c	2004-02-16 20:08:49.000000000 +0100
@@ -64,6 +64,8 @@
 krb5_boolean check_ticket_addresses;
 krb5_boolean allow_null_ticket_addresses;
 krb5_boolean allow_anonymous;
+int trpolicy;
+static const char *trpolicy_str;
 
 static struct getarg_strings addresses_str;	/* addresses to listen on */
 krb5_addresses explicit_addresses;
@@ -292,9 +294,8 @@
 
     get_dbinfo();
     
-    if(max_request_str){
+    if(max_request_str)
 	max_request = parse_bytes(max_request_str, NULL);
-    }
 
     if(max_request == 0){
 	p = krb5_config_get_string (context,
@@ -365,6 +366,23 @@
     allow_anonymous = 
 	krb5_config_get_bool(context, NULL, "kdc", 
 			     "allow-anonymous", NULL);
+    trpolicy_str = 
+	krb5_config_get_string_default(context, NULL, "always-check", "kdc", 
+				       "transited-policy", NULL);
+    if(strcasecmp(trpolicy_str, "always-check") == 0)
+	trpolicy = TRPOLICY_ALWAYS_CHECK;
+    else if(strcasecmp(trpolicy_str, "allow-per-principal") == 0)
+	trpolicy = TRPOLICY_ALLOW_PER_PRINCIPAL;
+    else if(strcasecmp(trpolicy_str, "always-honour-request") == 0)
+	trpolicy = TRPOLICY_ALWAYS_HONOUR_REQUEST;
+    else {
+	kdc_log(0, "unknown transited-policy: %s, reverting to always-check", 
+		trpolicy_str);
+	trpolicy = TRPOLICY_ALWAYS_CHECK;
+    }
+	
+	krb5_config_get_bool_default(context, NULL, TRUE, "kdc", 
+				     "enforce-transited-policy", NULL);
 #ifdef KRB4
     if(v4_realm == NULL){
 	p = krb5_config_get_string (context, NULL, 
diff -urN crypto/heimdal/kdc/kdc.8 heimdal-0.5.3/kdc/kdc.8
--- crypto/heimdal/kdc/kdc.8	2003-03-17 07:47:03.000000000 +0100
+++ crypto/heimdal/kdc/kdc.8	2004-02-16 18:50:11.000000000 +0100
@@ -2,5 +2,5 @@
 .\"
-.Dd August 22, 2002
+.Dd October 22, 2003
 .Dt KDC 8
 .Os HEIMDAL
 .Sh NAME
@@ -145,6 +145,27 @@
 check-ticket-addresses is TRUE.
 .It Li allow-anonymous = Va boolean
 Permit anonymous tickets with no addresses.
+.It Li transited-policy = Xo
+.Li always-check \*(Ba
+.Li allow-per-principal |
+.Li always-honour-request
+.Xc
+This controls how KDC requests with the
+.Li disable-transited-check
+flag are handled. It can be one of:
+.Bl -tag -width "xxx" -offset indent
+.It Li always-check
+Always check transited encoding, this is the default.
+.It Li allow-per-principal
+Currently this is identical to
+.Li always-check .
+In a future release, it will be possible to mark a principal as able
+to handle unchecked requests.
+.It Li always-honour-request
+Always do what the client asked.
+In a future release, it will be possible to force a check per
+principal.
+.El
 .It encode_as_rep_as_tgs_rep = Va boolean
 Encode AS-Rep as TGS-Rep to be bug-compatible with old DCE code.  The
 Heimdal clients allow both.
diff -u crypto/heimdal/kdc/kdc_locl.h:1.1.1.6 crypto/heimdal/kdc/kdc_locl.h:1.1.1.7
--- crypto/heimdal/kdc/kdc_locl.h:1.1.1.6	Thu Oct  9 14:36:19 2003
+++ crypto/heimdal/kdc/kdc_locl.h	Sat Apr  3 15:20:48 2004
@@ -62,6 +62,10 @@
 extern krb5_boolean check_ticket_addresses;
 extern krb5_boolean allow_null_ticket_addresses;
 extern krb5_boolean allow_anonymous;
+enum { TRPOLICY_ALWAYS_CHECK,
+       TRPOLICY_ALLOW_PER_PRINCIPAL, 
+       TRPOLICY_ALWAYS_HONOUR_REQUEST };
+extern int trpolicy;
 extern int enable_524;
 extern int enable_v4_cross_realm;
 
--- crypto/heimdal/kdc/kerberos5.c	9 Oct 2003 19:36:19 -0000	1.1.1.8
+++ crypto/heimdal/kdc/kerberos5.c	3 Apr 2004 21:20:51 -0000	1.1.1.9
@@ -496,8 +496,8 @@
     krb5_enctype cetype, setype;
     EncTicketPart et;
     EncKDCRepPart ek;
-    krb5_principal client_princ, server_princ;
-    char *client_name, *server_name;
+    krb5_principal client_princ = NULL, server_princ = NULL;
+    char *client_name = NULL, *server_name = NULL;
     krb5_error_code ret = 0;
     const char *e_text = NULL;
     krb5_crypto crypto;
@@ -506,27 +506,30 @@
     memset(&rep, 0, sizeof(rep));
 
     if(b->sname == NULL){
-	server_name = "<unknown server>";
 	ret = KRB5KRB_ERR_GENERIC;
 	e_text = "No server in request";
     } else{
 	principalname2krb5_principal (&server_princ, *(b->sname), b->realm);
 	krb5_unparse_name(context, server_princ, &server_name);
     }
+    if (ret) {
+	kdc_log(0, "AS-REQ malformed server name from %s", from);
+	goto out;
+    }
     
     if(b->cname == NULL){
-	client_name = "<unknown client>";
 	ret = KRB5KRB_ERR_GENERIC;
 	e_text = "No client in request";
     } else {
 	principalname2krb5_principal (&client_princ, *(b->cname), b->realm);
 	krb5_unparse_name(context, client_princ, &client_name);
     }
-    kdc_log(0, "AS-REQ %s from %s for %s", 
-	    client_name, from, server_name);
-
-    if(ret)
+    if (ret) {
+	kdc_log(0, "AS-REQ malformed client name from %s", from);
 	goto out;
+    }
+
+    kdc_log(0, "AS-REQ %s from %s for %s", client_name, from, server_name);
 
     ret = db_fetch(client_princ, &client);
     if(ret){
@@ -842,13 +845,8 @@
 	copy_HostAddresses(b->addresses, et.caddr);
     }
     
-    {
-	krb5_data empty_string;
-      
-	krb5_data_zero(&empty_string); 
-	et.transited.tr_type = DOMAIN_X500_COMPRESS;
-	et.transited.contents = empty_string;
-    }
+    et.transited.tr_type = DOMAIN_X500_COMPRESS;
+    krb5_data_zero(&et.transited.contents); 
      
     copy_EncryptionKey(&et.key, &ek.key);
 
@@ -930,9 +928,11 @@
 	ret = 0;
     }
   out2:
-    krb5_free_principal(context, client_princ);
+    if (client_princ)
+	krb5_free_principal(context, client_princ);
     free(client_name);
-    krb5_free_principal(context, server_princ);
+    if (server_princ)
+	krb5_free_principal(context, server_princ);
     free(server_name);
     if(client)
 	free_ent(client);
@@ -1055,33 +1055,35 @@
 }
 
 static krb5_error_code
-fix_transited_encoding(TransitedEncoding *tr, 
+fix_transited_encoding(krb5_boolean check_policy,
+		       TransitedEncoding *tr, 
+		       EncTicketPart *et, 
 		       const char *client_realm, 
 		       const char *server_realm, 
 		       const char *tgt_realm)
 {
     krb5_error_code ret = 0;
-    if(strcmp(client_realm, tgt_realm) && strcmp(server_realm, tgt_realm)){
-	char **realms = NULL, **tmp;
-	int num_realms = 0;
-	int i;
-	if(tr->tr_type && tr->contents.length != 0) {
-	    if(tr->tr_type != DOMAIN_X500_COMPRESS){
-		kdc_log(0, "Unknown transited type: %u", 
-			tr->tr_type);
-		return KRB5KDC_ERR_TRTYPE_NOSUPP;
-	    }
-	    ret = krb5_domain_x500_decode(context, 
-					  tr->contents,
-					  &realms, 
-					  &num_realms,
-					  client_realm,
-					  server_realm);
-	    if(ret){
-		krb5_warn(context, ret, "Decoding transited encoding");
-		return ret;
-	    }
-	}
+    char **realms, **tmp;
+    int num_realms;
+    int i;
+
+    if(tr->tr_type != DOMAIN_X500_COMPRESS) {
+	kdc_log(0, "Unknown transited type: %u", tr->tr_type);
+	return KRB5KDC_ERR_TRTYPE_NOSUPP;
+    }
+
+    ret = krb5_domain_x500_decode(context, 
+				  tr->contents,
+				  &realms, 
+				  &num_realms,
+				  client_realm,
+				  server_realm);
+    if(ret){
+	krb5_warn(context, ret, "Decoding transited encoding");
+	return ret;
+    }
+    if(strcmp(client_realm, tgt_realm) && strcmp(server_realm, tgt_realm)) {
+	/* not us, so add the previous realm to transited set */
 	if (num_realms < 0 || num_realms + 1 > UINT_MAX/sizeof(*realms)) {
 	    ret = ERANGE;
 	    goto free_realms;
@@ -1098,16 +1100,46 @@
 	    goto free_realms;
 	}
 	num_realms++;
-	free_TransitedEncoding(tr);
-	tr->tr_type = DOMAIN_X500_COMPRESS;
-	ret = krb5_domain_x500_encode(realms, num_realms, &tr->contents);
-	if(ret)
-	    krb5_warn(context, ret, "Encoding transited encoding");
-    free_realms:
+    }
+    if(num_realms == 0) {
+	if(strcmp(client_realm, server_realm)) 
+	    kdc_log(0, "cross-realm %s -> %s", client_realm, server_realm);
+    } else {
+	size_t l = 0;
+	char *rs;
 	for(i = 0; i < num_realms; i++)
-	    free(realms[i]);
-	free(realms);
+	    l += strlen(realms[i]) + 2;
+	rs = malloc(l);
+	if(rs != NULL) {
+	    *rs = '\0';
+	    for(i = 0; i < num_realms; i++) {
+		if(i > 0)
+		    strlcat(rs, ", ", l);
+		strlcat(rs, realms[i], l);
+	    }
+	    kdc_log(0, "cross-realm %s -> %s via [%s]", client_realm, server_realm, rs);
+	    free(rs);
+	}
     }
+    if(check_policy) {
+	ret = krb5_check_transited(context, client_realm, 
+				   server_realm, 
+				   realms, num_realms, NULL);
+	if(ret) {
+	    krb5_warn(context, ret, "cross-realm %s -> %s", 
+		      client_realm, server_realm);
+	    goto free_realms;
+	}
+	et->flags.transited_policy_checked = 1;
+    }
+    et->transited.tr_type = DOMAIN_X500_COMPRESS;
+    ret = krb5_domain_x500_encode(realms, num_realms, &et->transited.contents);
+    if(ret)
+	krb5_warn(context, ret, "Encoding transited encoding");
+  free_realms:
+    for(i = 0; i < num_realms; i++)
+	free(realms[i]);
+    free(realms);
     return ret;
 }
 
@@ -1175,8 +1207,28 @@
     if(ret)
 	goto out;
 
-    copy_TransitedEncoding(&tgt->transited, &et.transited);
-    ret = fix_transited_encoding(&et.transited,
+    /* We should check the transited encoding if:
+       1) the request doesn't ask not to be checked
+       2) globally enforcing a check
+       3) principal requires checking
+       4) we allow non-check per-principal, but principal isn't marked as allowing this
+       5) we don't globally allow this
+    */
+
+#define GLOBAL_FORCE_TRANSITED_CHECK		(trpolicy == TRPOLICY_ALWAYS_CHECK)
+#define GLOBAL_ALLOW_PER_PRINCIPAL		(trpolicy == TRPOLICY_ALLOW_PER_PRINCIPAL)
+#define GLOBAL_ALLOW_DISABLE_TRANSITED_CHECK	(trpolicy == TRPOLICY_ALWAYS_HONOUR_REQUEST)
+/* these will consult the database in future release */
+#define PRINCIPAL_FORCE_TRANSITED_CHECK(P)		0
+#define PRINCIPAL_ALLOW_DISABLE_TRANSITED_CHECK(P)	0
+
+    ret = fix_transited_encoding(!f.disable_transited_check ||
+				 GLOBAL_FORCE_TRANSITED_CHECK ||
+				 PRINCIPAL_FORCE_TRANSITED_CHECK(server) ||
+				 !((GLOBAL_ALLOW_PER_PRINCIPAL && 
+				    PRINCIPAL_ALLOW_DISABLE_TRANSITED_CHECK(server)) ||
+				   GLOBAL_ALLOW_DISABLE_TRANSITED_CHECK),
+				 &tgt->transited, &et,
 				 *krb5_princ_realm(context, client_principal),
 				 *krb5_princ_realm(context, server->principal),
 				 *krb5_princ_realm(context, krbtgt->principal));
@@ -1276,7 +1328,7 @@
        DES3? */
     ret = encode_reply(&rep, &et, &ek, etype, adtkt ? 0 : server->kvno, ekey,
 		       0, &tgt->key, e_text, reply);
-out:
+  out:
     free_TGS_REP(&rep);
     free_TransitedEncoding(&et.transited);
     if(et.starttime)
@@ -1378,13 +1430,13 @@
 }
 
 static Realm
-find_rpath(Realm r)
+find_rpath(Realm crealm, Realm srealm)
 {
     const char *new_realm = krb5_config_get_string(context,
 						   NULL,
-						   "libdefaults", 
-						   "capath", 
-						   r, 
+						   "capaths", 
+						   crealm,
+						   srealm,
 						   NULL);
     return (Realm)new_realm;
 }
@@ -1676,7 +1728,7 @@
 
 	    if ((req_rlm = get_krbtgt_realm(&sp->name)) != NULL) {
 		if(loop++ < 2) {
-		    new_rlm = find_rpath(req_rlm);
+		    new_rlm = find_rpath(tgt->crealm, req_rlm);
 		    if(new_rlm) {
 			kdc_log(5, "krbtgt for realm %s not found, trying %s", 
 				req_rlm, new_rlm);
@@ -1724,6 +1776,18 @@
 	    goto out;
 	}
 #endif
+
+	if(strcmp(krb5_principal_get_realm(context, sp),
+		  krb5_principal_get_comp_string(context, krbtgt->principal, 1)) != 0) {
+	    char *tpn;
+	    ret = krb5_unparse_name(context, krbtgt->principal, &tpn);
+	    kdc_log(0, "Request with wrong krbtgt: %s", (ret == 0) ? tpn : "<unknown>");
+	    if(ret == 0)
+		free(tpn);
+	    ret = KRB5KRB_AP_ERR_NOT_US;
+	    goto out;
+	    
+	}
 
 	ret = check_flags(client, cpn, server, spn, FALSE);
 	if(ret)
diff -urN crypto/heimdal/lib/krb5/krb5-protos.h heimdal-0.5.3/lib/krb5/krb5-protos.h
--- crypto/heimdal/lib/krb5/krb5-protos.h	2003-03-17 11:27:40.000000000 +0100
+++ crypto/heimdal/lib/krb5/krb5-protos.h	2004-04-01 16:16:33.000000000 +0200
@@ -521,6 +521,15 @@
 	krb5_data */*result_string*/);
 
 krb5_error_code
+krb5_check_transited (
+	krb5_context /*context*/,
+	krb5_const_realm /*client_realm*/,
+	krb5_const_realm /*server_realm*/,
+	krb5_realm */*realms*/,
+	int /*num_realms*/,
+	int */*bad_realm*/);
+
+krb5_error_code
 krb5_check_transited_realms (
 	krb5_context /*context*/,
 	const char *const */*realms*/,
diff -urN crypto/heimdal/lib/krb5/rd_req.c heimdal-0.5.3/lib/krb5/rd_req.c
--- crypto/heimdal/lib/krb5/rd_req.c	2001-06-18 04:48:18.000000000 +0200
+++ crypto/heimdal/lib/krb5/rd_req.c	2004-02-16 19:17:47.000000000 +0100
@@ -129,6 +129,32 @@
     return 0;
 }
 
+static krb5_error_code
+check_transited(krb5_context context, Ticket *ticket, EncTicketPart *enc)
+{
+    char **realms;
+    int num_realms;
+    krb5_error_code ret;
+	    
+    if(enc->transited.tr_type != DOMAIN_X500_COMPRESS)
+	return KRB5KDC_ERR_TRTYPE_NOSUPP;
+
+    if(enc->transited.contents.length == 0)
+	return 0;
+
+    ret = krb5_domain_x500_decode(context, enc->transited.contents, 
+				  &realms, &num_realms, 
+				  enc->crealm,
+				  ticket->realm);
+    if(ret)
+	return ret;
+    ret = krb5_check_transited(context, enc->crealm, 
+			       ticket->realm, 
+			       realms, num_realms, NULL);
+    free(realms);
+    return ret;
+}
+
 krb5_error_code
 krb5_decrypt_ticket(krb5_context context,
 		    Ticket *ticket,
@@ -161,6 +187,14 @@
 	    krb5_clear_error_string (context);
 	    return KRB5KRB_AP_ERR_TKT_EXPIRED;
 	}
+	
+	if(!t.flags.transited_policy_checked) {
+	    ret = check_transited(context, ticket, &t);
+	    if(ret) {
+		free_EncTicketPart(&t);
+		return ret;
+	    }
+	}
     }
     
     if(out)
@@ -209,29 +243,6 @@
     return ret;
 }
 
-#if 0
-static krb5_error_code
-check_transited(krb5_context context,
-		krb5_ticket *ticket)
-{
-    char **realms;
-    int num_realms;
-    krb5_error_code ret;
-
-    if(ticket->ticket.transited.tr_type != DOMAIN_X500_COMPRESS)
-	return KRB5KDC_ERR_TRTYPE_NOSUPP;
-
-    ret = krb5_domain_x500_decode(ticket->ticket.transited.contents, 
-				  &realms, &num_realms, 
-				  ticket->client->realm,
-				  ticket->server->realm);
-    if(ret)
-	return ret;
-    ret = krb5_check_transited_realms(context, realms, num_realms, NULL);
-    free(realms);
-    return ret;
-}
-#endif
 
 krb5_error_code
 krb5_verify_ap_req(krb5_context context,
diff -urN crypto/heimdal/lib/krb5/transited.c heimdal-0.5.3/lib/krb5/transited.c
--- crypto/heimdal/lib/krb5/transited.c	2002-09-09 16:03:03.000000000 +0200
+++ crypto/heimdal/lib/krb5/transited.c	2004-02-16 19:20:52.000000000 +0100
@@ -304,6 +304,12 @@
     struct tr_realm *p, **q;
     int ret;
     
+    if(tr.length == 0) {
+	*realms = NULL;
+	*num_realms = 0;
+	return 0;
+    }
+
     /* split string in components */
     ret = decode_realms(context, tr.data, tr.length, &r);
     if(ret)
@@ -358,6 +364,9 @@
     char *s = NULL;
     int len = 0;
     int i;
+    krb5_data_zero(encoding);
+    if (num_realms == 0)
+	return 0;
     for(i = 0; i < num_realms; i++){
 	len += strlen(realms[i]);
 	if(realms[i][0] == '/')
@@ -365,6 +374,8 @@
     }
     len += num_realms - 1;
     s = malloc(len + 1);
+    if (s == NULL)
+	return ENOMEM;
     *s = '\0';
     for(i = 0; i < num_realms; i++){
 	if(i && i < num_realms - 1)
@@ -379,6 +390,44 @@
 }
 
 krb5_error_code
+krb5_check_transited(krb5_context context,
+		     krb5_const_realm client_realm,
+		     krb5_const_realm server_realm,
+		     krb5_realm *realms,
+		     int num_realms,
+		     int *bad_realm)
+{
+    char **tr_realms;
+    char **p;
+    int i;
+
+    if(num_realms == 0)
+	return 0;
+    
+    tr_realms = krb5_config_get_strings(context, NULL, 
+					"capaths", 
+					client_realm, 
+					server_realm, 
+					NULL);
+    for(i = 0; i < num_realms; i++) {
+	for(p = tr_realms; p && *p; p++) {
+	    if(strcmp(*p, realms[i]) == 0)
+		break;
+	}
+	if(p == NULL || *p == NULL) {
+	    krb5_config_free_strings(tr_realms);
+	    krb5_set_error_string (context, "no transit through realm %s",
+				   realms[i]);
+	    if(bad_realm)
+		*bad_realm = i;
+	    return KRB5KRB_AP_ERR_ILL_CR_TKT;
+	}
+    }
+    krb5_config_free_strings(tr_realms);
+    return 0;
+}
+
+krb5_error_code
 krb5_check_transited_realms(krb5_context context,
 			    const char *const *realms, 
 			    int num_realms, 
